/**
 * 这个包是专门放shiro权限管理框架  所需要写的类的：
 *      如：一、自定义realm类（去数据库验证数据  代替shiro默认的去配置文件shiro.ini中去验证数据）
 *          二、SpringBoot整合shiro的配置【配置类】  需要装配（@Bean）项目中对应的要验证信息的对象
 *          以及设置shiro提供的过滤器的拦截规则
 * */
package com.baizhi.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @Configuration 声明当前类为一个配置类
 * SpringBoot整合shiro需要的配置类：主要要解决两个问题
 * 一是：装配自己写的shiro自定义realm类  指明要从数据库中查询数据来认证（写好了该类需要装配集成到SpringBoot中）
 * 【因为shiro默认的是去配置文件shiro.ini中查询数据进行认证和授权，我们需要的是从数据库中查数据进行认证和授权】
 * 二是：shiro框架提供了过滤器工厂的API  我们可以获取它并设置过滤规则 实现想要的功能（如：强制登陆等）
 */
@Configuration
public class ShiroConfig {
    /**
     * 0补充：密码校验规则HashedCredentialsMatcher
     * 配置项目的shiro加密规则
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //指定加密方式为MD5   其它还有sha3  等等很多
        credentialsMatcher.setHashAlgorithmName("MD5");
        //指定加密次数  即散列的次数
        credentialsMatcher.setHashIterations(1024);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
        /*
         * 注：配置好加密规则  记得在下面创建的自定义realm对象中  装配（注入）该加密规则
         * */
    }

    /**
     * 一是：创建自定义Realm对象  将其装配到项目中  @Bean注解是Spring项目中装配对象的
     */
    /*
    注意：这个自定义ShiroRealm类是    认证所需的realm类
        而我们写了授权realm类(AuthorizingRealm)后,就需要用AuthorizingRealm授权类取代此认证realm类
        因为授权的realm类中包含了认证realm类中的方法（认证内容）
        AuthorizingRealm授权realm类和认证realm类的配置方法一模一样
    @Bean
    public ShiroRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
        ShiroRealm realm = new ShiroRealm();
        //让自定义realm使用新的加密规则
        realm.setCredentialsMatcher(hashedCredentialsMatcher);
        return  realm;
    }*/
    @Bean
    public AuthorRealm authorRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
        AuthorRealm authorRealm = new AuthorRealm();
        //给授权realm类配置上加密规则
        authorRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        //返回
        return authorRealm;
    }


    /**
     * 然后，创建并装配安全管理器对象
     *      将自定义的授权realm类对象装配到安全管理器中
     *      （所有这里需要给创建安全管理器的方法中 添加一个authorRealm对象形参）
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(AuthorRealm authorRealm){
        //new一个安全管理器对象
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //将自定义realm对象设置到安全管理器中
        securityManager.setRealm(authorRealm);
        //返回
        return securityManager;
    }

    /**
     *  开启shiro aop注解支持
     *  使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
     *  参数是：安全管理器对象
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 最后，创建过滤器工厂
     *      第一：要配置过滤规则
     *      第二：配置安全管理器（这里需要给创建过滤器工厂的方法中  添加一个安全管理器securityManager形参）
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        //首先  创建过滤器工厂对象  new出来的
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();

        //默认的登陆（认证）页面是在webapp根目录的下  若是登陆页面不在这个webapp根目录下 需要在此设置登陆路径
        filterFactoryBean.setLoginUrl("/login.jsp");
        //然后  准备一个hashmap  用来设置过滤规则
        Map map = new HashMap();
        /**  设置拦截规则：用要设置的资源路径为map的key  拦截authc或不拦截anon 为map的value
         * anon 代表匿名可访问 就是不用登录就可以访问
         * authc 代表登录后才能访问
         * 支持通配符*
         * 注意拦截规则 一个一个配置  不能直接拦截所有  即：拦截 /*  是一定不能使用的
         */
        map.put("/login.jsp","anon");
        map.put("/error/*","anon");//错误页面不能被拦截
        map.put("/cmfzAdmin/*","anon");

        map.put("/main2.jsp","authc");
        map.put("/jsp/*","authc");
        map.put("/menu/*","authc");
        map.put("/cmfzUser/*","authc");
        map.put("/cmfzLog/*","authc");
        map.put("/cmfzGuru/*","authc");
        map.put("/cmfzBanner/*","authc");
        /**
         * 配置拦截规则的方法：  setFilterChainDefinitionMap(map)
         * 参数为map 在Map中配置拦截规则
         * - 哪些页面和url拦截  authc
         * - 哪些页面和url不拦截 anon
         */
        filterFactoryBean.setFilterChainDefinitionMap(map);

        //第三、配置安全管理器
        filterFactoryBean.setSecurityManager(securityManager);

        //最后、返回
        return filterFactoryBean;
    }
}
